using System.Collections.ObjectModel;

namespace Bread.Utility;

public class AsyncWorkQueue<T> : IDisposable
       where T : IAsyncWorkItem
{
    public event Action<Exception>? Interrupted;

    /// <summary>
    /// 最大并发数
    /// </summary>
    public int MaxParallerCount { get; set; } = 1;



    public ReadOnlyCollection<T> Items { get { return _items.AsReadOnly(); } }

    /// <summary>
    /// 动作过滤器，只执行最新加入的动作
    /// </summary>
    public Action<IList<T>> DefaultFilter { get; } = RemoveSameItemFilter;

    List<T> _items = new List<T>();
    object _itemsLocker = new object();

    Thread _thread;
    AutoResetEvent _waitTaskEvent = new AutoResetEvent(false);
    AutoResetEvent _waitForCloseEvent = new AutoResetEvent(false);

    public AsyncWorkQueue()
    {
        _thread = new Thread(new ThreadStart(ThreadMain));
        _thread.Start();
    }

    private Func<bool>? _enableActor = null;
    public void SetEnable(Func<bool> enableActor)
    {
        _enableActor = enableActor;
    }

    public void QueueWork(T item, Action<IList<T>>? filter = null)
    {
        if (_isClosed) throw new InvalidOperationException("thread has exited.");

        lock (_itemsLocker) {
            _items.Add(item);
            if (filter != null) filter(_items);
        }

        _waitTaskEvent.Set();
    }

    private static void RemoveSameItemFilter(IList<T> items)
    {
        int count = items.Count;
        if (count <= 1) return;
        var tag = items[count - 1].Tag;
        if (string.IsNullOrWhiteSpace(tag)) return;

        for (int i = count - 2; i >= 0; i--) {
            var item = items[i];
            if (item.State.IsStarted || item.State.IsCompleted || item.State.IsCanceled) break;
            if (item.Tag != tag) break;
            items.RemoveAt(i);
        }
    }

    /// <summary>
    /// enumerate element
    /// </summary>
    /// <param name="actor"></param>
    public void ForEach(Action<T> actor)
    {
        if (actor == null) return;

        lock (_itemsLocker) {
            foreach (var item in _items) {
                actor(item);
            }
        }
    }

    /// <summary>
    /// enumerate element, break when return true
    /// </summary>
    /// <param name="func"></param>
    public void ForEach(Func<T, bool> func)
    {
        if (func == null) return;

        lock (_itemsLocker) {
            foreach (var item in _items) {
                if (func(item)) break;
            }
        }
    }


    public void CancleAsync(T target)
    {
        lock (_itemsLocker) {
            foreach (var item in _items) {
                if (target.Equals(item)) {
                    item.CancelAsync();
                }
            }
        }
    }

    public void PauseAsync(T target)
    {
        lock (_itemsLocker) {
            foreach (var item in _items) {
                if (target.Equals(item)) {
                    item.PauseAsync();
                }
            }
        }
    }

    public void ResumeAsync(T target)
    {
        lock (_itemsLocker) {
            foreach (var item in _items) {
                if (target.Equals(item)) {
                    item.ResumeAsync();
                }
            }
        }
    }


    public void Clear()
    {
        lock (_itemsLocker) {
            foreach (var item in _items) {
                item.CancelAsync();
            }
            _items.Clear();
        }
    }

    bool _isClosed = false;
    bool _isClosing = false;

    public void Close()
    {
        if (_isClosed) return;
        if (_thread == null) return;

        _isClosing = true;
        _waitTaskEvent.Set();

        int index = 0;
        while (index < 100) {
            if (_isClosed) break;
            _waitForCloseEvent.WaitOne(50);
            index++;
        }

        Clear();

        _isClosing = false;
        _isClosed = true;
    }


    private void ThreadMain()
    {
        int waitTime = 100;
        try {
            while (true) {
                // run
                if (_isClosing) {
                    _isClosed = true;
                    _waitForCloseEvent.Set();
                    return;
                }

                _waitTaskEvent.WaitOne(waitTime);

                //task
                if (_items.Count == 0) {
                    waitTime = 600;
                    continue;
                }

                if (_enableActor != null) {
                    if (_enableActor() == false) {
                        waitTime = 100;
                        continue;
                    }
                }

                lock (_itemsLocker) {
                    for (int i = 0; i < MaxParallerCount;) {
                        if (i == _items.Count) break;
                        if (_items.Count == 0) break;

                        if (_enableActor != null) {
                            if (_enableActor() == false) {
                                waitTime = 100;
                                break;
                            }
                        }

                        var item = _items[i];
                        if (item.State.IsCanceled || item.State.IsCompleted) {
                            _items.RemoveAt(i);
                            continue;
                        }

                        if (!item.State.IsStarted) {
                            item.StartAsync();
                        }

                        i++;
                    }
                }

                waitTime = 100;
            }
        }
        catch (Exception ex) {
            Interrupted?.Invoke(ex);
            Log.Error($"任务队列异常退出，{ex.Message}");
            Log.Exception(ex);
        }
        _isClosed = true;
    }

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue) {
            Close();
            _waitTaskEvent.Dispose();
            _waitForCloseEvent.Dispose();
            disposedValue = true;
        }
    }

    ~AsyncWorkQueue()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion

}